﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MvcEngine.Blog.Models.Entities;
using MvcEngine.Blog.Repositories;
using MvcEngine.Core;
using MvcEngine.Core.Helpers;
using MvcEngine.Core.Repositories;
using MvcEngine.Blog.Components.Services;
using MvcEngine.Core.Utils;
using System.Linq.Expressions;
using MvcEngine.Core.Extensions;
using MvcEngine.Mvc.Models;
using MvcEngine.Core.Pagination;

namespace MvcEngine.Blog.Managers
{
    public class BlogPostManager : IBlogPostManager
    {
        private readonly IBlogPostRepository postRepository = null;
        private readonly IBlogPostCategoryRepository postCategoryRepository = null;
        private readonly IBlogPostTagRepository postTagRepository = null;
        private readonly IBlogPostCommentRepository commentRepository = null;
        private readonly IBlogPostNotifyRepository notifyRepository = null;


        public BlogPostManager(IBlogPostRepository postRepository, IBlogPostCategoryRepository postCategoryRepository, IBlogPostTagRepository postTagRepository, IBlogPostCommentRepository commentRepository, IBlogPostNotifyRepository notifyRepository)
        {
            this.postRepository = postRepository;
            this.postCategoryRepository = postCategoryRepository;
            this.postTagRepository = postTagRepository;
            this.commentRepository = commentRepository;
            this.notifyRepository = notifyRepository;
        }

        public static IBlogPostManager Current
        {
            get { return IoC.Resolve<IBlogPostManager>(); }
        }

        public void DeletePost(Guid postId)
        {
            using (IUnitOfWork unitOfWork = UnitOfWorkHelper.Get())
            {
                postCategoryRepository.DeleteAll(postId);
                postTagRepository.DeleteAll(postId);
                commentRepository.DeleteAll(postId);
                notifyRepository.DeleteAll(postId);
                postRepository.Delete(postId);
                unitOfWork.Commit();
            }
            CacheManager.Current.ClearCache(BlogCacheKeys.PostKeys.Category);
        }

        public void DeletePosts(Guid blogId)
        {
            postRepository.DeleteAll(blogId);
            CacheManager.Current.ClearCache(BlogCacheKeys.PostKeys.Category);
        }

        public IPagination<BlogPost> GetPosts(Guid blogId, int pageNumber, int postsPerPage, bool visibleOnly)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostBlogPaging, blogId, pageNumber, postsPerPage), BlogCacheKeys.PostKeys.Category,
                () => postRepository.GetPosts(blogId, pageNumber, postsPerPage, true));
        }

        public IPagination<BlogPost> GetPosts(Guid blogId, int pageNumber, int postsPerPage)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostBlogPaging, blogId, pageNumber, postsPerPage), BlogCacheKeys.PostKeys.Category,
                () => postRepository.GetPosts(blogId, pageNumber, postsPerPage));
        }

        public IPagination<BlogPost> GetPostsByTag(Guid tagId, int pageNumber, int postsPerPage)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostTagPaging, tagId, pageNumber, postsPerPage), BlogCacheKeys.PostKeys.Category,
                () => postRepository.GetPostsByTag(tagId, pageNumber, postsPerPage));
        }

        public IPagination<BlogPost> GetPostsByCategory(Guid categoryId, int pageNumber, int postsPerPage)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostCategoryPaging, categoryId, pageNumber, postsPerPage), BlogCacheKeys.PostKeys.Category,
                () => postRepository.GetPostsByCategory(categoryId, pageNumber, postsPerPage));
        }

        public IList<BlogPost> GetPosts(Guid blogId, int count)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostCount, blogId, count), BlogCacheKeys.PostKeys.Category,
                () => postRepository.GetPosts(blogId, count).ToList());
        }

        public BlogPost GetPost(Guid postId)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostId, postId), BlogCacheKeys.PostKeys.Category,
                () => postRepository.Get(postId));
        }

        public BlogPost GetPost(string postName, Guid blogId)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.PostKeys.PostName, postName, blogId), BlogCacheKeys.PostKeys.Category,
                () => postRepository.Get(postName, blogId, !UserContext.Current.User.IsAdmin()));
        }

        public void SavePost(BlogPost post, IList<BlogPostCategory> categories, IList<BlogPostTag> tags)
        {
            Guard.ThrowIfEmptyGuid(post.Id);
            bool isUpdated = true;
            using (var unitOfWork = UnitOfWorkHelper.Get())
            {
                BlogPost postToSave = null;
                if (post.Id != Guid.Empty)
                {
                    postToSave = postRepository.Get(post.Id);
                }

                if (postToSave == null)
                {
                    if (post.Id == Guid.Empty)
                    {
                        post.Id = Guid.NewGuid();
                    }
                    postToSave = new BlogPost { Id = post.Id };
                    postToSave.CreatedDate = post.CreatedDate;
                    isUpdated = false;
                }

                postToSave.Title = post.Title;
                postToSave.BlogId = post.BlogId;
                postToSave.Author = post.Author;
                postToSave.Rating = post.Rating;
                postToSave.Description = post.Description;
                postToSave.Keywords = post.Keywords;
                postToSave.PublishedDate = post.PublishedDate;
                postToSave.IsCommentEnabled = post.IsCommentEnabled;
                postToSave.Slug = post.Slug;
                postToSave.CreatedDate = postToSave.CreatedDate == DateTime.MinValue ? DateTime.Now : postToSave.CreatedDate;
                postToSave.ModifiedDate = DateTime.Now;
                postToSave.PostContent = post.PostContent;



                if (isUpdated)
                {
                    postRepository.Update(postToSave);

                    var oldCategories = postCategoryRepository.GetAll(post.Id);
                    var oldTags = postTagRepository.GetAll(post.Id);

                    var newCategories = from c in categories
                                        where !oldCategories.Any(i => i.CategoryId == c.Id && i.PostId == post.Id)
                                        select c;

                    var deletedCategories = from c in oldCategories
                                            where !categories.Any(i => i.CategoryId == c.Id && i.PostId == post.Id)
                                            select c;

                    var newTags = from c in tags
                                  where !oldTags.Any(i => i.TagId == c.Id && i.PostId == post.Id)
                                  select c;

                    var deletedTags = from c in oldTags
                                      where !tags.Any(i => i.TagId == c.Id && i.PostId == post.Id)
                                      select c;

                    foreach (var item in newCategories)
                    {
                        item.CreatedDate = DateTime.Now;
                        item.ModifiedDate = DateTime.Now;
                    }

                    foreach (var item in newTags)
                    {
                        item.CreatedDate = DateTime.Now;
                        item.ModifiedDate = DateTime.Now;
                    }

                    postCategoryRepository.InsertAll(newCategories);
                    postCategoryRepository.DeleteAll(deletedCategories);
                    postTagRepository.InsertAll(newTags);
                    postTagRepository.DeleteAll(deletedTags);
                }
                else
                {
                    postRepository.Add(postToSave);
                    foreach (var item in categories)
                    {
                        item.ModifiedDate = DateTime.Now;
                        item.CreatedDate = DateTime.Now;
                    }
                    foreach (var item in tags)
                    {
                        item.ModifiedDate = DateTime.Now;
                        item.CreatedDate = DateTime.Now;
                    }

                    postCategoryRepository.InsertAll(categories);
                    postTagRepository.InsertAll(tags);
                }
                unitOfWork.Commit();
            }
            CacheManager.Current.ClearCache(BlogCacheKeys.PostKeys.Category);
            CacheManager.Current.ClearCache(BlogCacheKeys.CategoryKeys.Category);
        }

        #region Comments

        public IList<BlogPostComment> GetComments(Guid postId)
        {
            return commentRepository.GetAll(postId);
        }

        public void SaveComment(BlogPostComment comment)
        {
            comment.CreatedDate = DateTime.Now;
            comment.ModifiedDate = DateTime.Now;
            commentRepository.Add(comment);
            CacheManager.Current.ClearCache(BlogCacheKeys.PostKeys.Category);
        }

        public void DeleteComment(Guid commentId)
        {
            var comment = GetComment(commentId);
            if (comment != null)
            {
                commentRepository.Delete(commentId);
                CacheManager.Current.ClearCache(BlogCacheKeys.PostKeys.Category);
            }
        }

        public BlogPostComment GetComment(Guid commentId)
        {
            return CacheManager.Current.FromCache(string.Format(BlogCacheKeys.Comment.CommentId, commentId), BlogCacheKeys.Comment.Category,
                    () => commentRepository.Get(commentId));
        }

        #endregion
    }
}
